﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using OrganizationName.CodeGeneration;
using System.Reflection;

namespace OrganizationName.Factory
{
    public class Invoke : AnonymousInstance
    {
        public virtual Source Source { get; set; }
        public virtual string Method { get; set; }

        public override void InitializationPhase(ICodeContext codeContext)
        {
            ICodeContext sourceContext = codeContext.CreateChild(Source);
            Source.GenerateCode(sourceContext);

            CodeExpression[] childParameters = GenerateChildInstances(codeContext, Parameter);

            CodeExpression methodInvokeExpression;

            if (Method == "[]")
            {
                methodInvokeExpression = new CodeIndexerExpression
                (
                    Source.ToRightHandSideExpression(codeContext),
                    childParameters
                );
            }
            else
            {
                methodInvokeExpression = new CodeMethodInvokeExpression
                (
                    Source.ToRightHandSideExpression(codeContext),
                    Method,
                    childParameters
                );
            }

            if (!IsVoid(codeContext))
            {
                codeContext.Statements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression(UniqueId), methodInvokeExpression));
            }
            else
            {
                codeContext.Statements.Add(methodInvokeExpression);
            }
        }

        public override System.Type GetAbstractType(ICodeContext codeContext)
        {
            return GetConcreteType(codeContext);
        }

        public override System.Type GetConcreteType(ICodeContext codeContext)
        {
            ICodeContext sourceContext = codeContext.CreateChild(Source);
            System.Type sourceType = Source.GetConcreteType(sourceContext);

            string methodName = Method;
            if (methodName == "[]")
            {
                methodName = "get_Item";
            }
            MethodInfo methodInfo = sourceType.GetMethod(methodName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            if (methodInfo == null)
            {
                throw new CodeGenerationException("The type " + sourceType + " does not contain a public instance method with the name " + methodName, sourceContext, null);
            }
            return methodInfo.ReturnType;
        }
    }
}
